package Logika;

import java.util.Random;
import java.util.Vector;
import telebim.MainWindow;

/**
 *
 * @author mejcu
 */
public class MindServer {

    /**
     *
     */
    public int[][] rozkladPlansza;
    private boolean[][] boolPlansza;
    private int[][] rozkladTank = new int[Zmienne.SZEROKOSC][Zmienne.WYSOKOSC];
    private Vector<Tank> clientTank = new Vector<Tank>();
    private Vector<String> clientAddress = new Vector<String>();
    private Kodowanie kod = new Kodowanie();
    private Random random = new Random();
    private int[] values = new int[3];
    private SendToAllListener listener;
    /**
     *
     */
    public boolean[] deadPlayers = {false, false, false, false};
    private boolean freeToKill[] = {true, true, true, true};
    Pocisk[] pociski = {null, null, null, null};
    /**
     *
     */
    public boolean state;

    /**
     *
     */
    public MindServer(SendToAllListener _listener) {
        listener = _listener;
        rozkladPlansza = new int[][]// layout of the game
                {
                    {2, 2, 2, 0, 0, 0, 1, 0, 0, 0, 0, 0},
                    {3, 3, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0},
                    {0, 7, 7, 7, 1, 3, 0, 7, 7, 7, 7, 0},
                    {0, 3, 3, 3, 1, 3, 3, 3, 3, 3, 1, 0},
                    {1, 3, 3, 3, 1, 3, 0, 3, 3, 1, 3, 0},
                    {1, 3, 7, 7, 7, 7, 0, 3, 1, 3, 3, 0},
                    {1, 3, 7, 2, 7, 3, 0, 7, 7, 7, 7, 0},
                    {1, 3, 7, 7, 7, 3, 0, 3, 3, 3, 3, 0},
                    {0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0},
                    {0, 7, 3, 3, 3, 7, 0, 7, 7, 7, 3, 1},
                    {0, 7, 7, 3, 7, 7, 0, 7, 3, 3, 3, 1},
                    {0, 7, 3, 1, 3, 1, 0, 1, 1, 3, 3, 1},
                    {0, 7, 3, 3, 3, 1, 0, 7, 3, 3, 3, 0},
                    {0, 7, 3, 3, 3, 7, 0, 7, 7, 7, 3, 0},
                    {0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 2},
                    {0, 0, 0, 1, 1, 0, 0, 0, 0, 2, 2, 2}
                };


        boolPlansza = new boolean[Zmienne.getSZEROKOSC()][Zmienne.getWYSOKOSC()];// boolPlansza very helpful during Mind work

        for (int i = 0; i < Zmienne.SZEROKOSC; i++) {
            for (int j = 0; j < Zmienne.WYSOKOSC; j++) {
                rozkladTank[i][j] = -1;
                if ((rozkladPlansza[i][j] == Zmienne.getFREE()) || (rozkladPlansza[i][j] == Zmienne.getWOOD())) {
                    boolPlansza[i][j] = true;
                }
            }
        }
    }

    /**
     *
     * @param x
     * @param y
     * @param value
     */
    public void setBonus(int x, int y, int value) {
        rozkladPlansza[x][y] = value;
    }

    /**
     *
     * @return
     */
    public int[] getNewTankPosition() {
        int[] initialValues = new int[3];// first-polozenieX, second-polozenieY, third-direction
        while (true) {
            initialValues[0] = random.nextInt(Zmienne.SZEROKOSC);
            initialValues[1] = random.nextInt(Zmienne.WYSOKOSC);
            if (boolPlansza[initialValues[0]][initialValues[1]]) {
                initialValues[2] = random.nextInt(4) + 1;
                break;
            }
        }
        return initialValues;
    }

    /*
     * Move Method analizes data given to server and produces the output data
     *
     */
    /**
     *
     * @param data
     * @param TankAddress
     * @return
     */
    public String move(String data, String TankAddress) {
        Tank tank = (Tank) clientTank.elementAt(clientAddress.indexOf(TankAddress));
        String komunikat = "";

        if ((int) data.charAt(0) != tank.zwrocKierunek()) {
            komunikat = rotate(tank.zwrocPolozenieX(), tank.zwrocPolozenieY(), (int) data.charAt(0));
            tank.pobierzZwrot((int) data.charAt(0));
        } else {
            int dx = 0, dy = 0;
            switch ((int) data.charAt(0)) {
                case Zmienne.TURN_NORTH:
                    dx = -1;
                    tank.pobierzZwrot(Zmienne.TURN_NORTH);
                    break;
                case Zmienne.TURN_EAST:
                    dy = 1;
                    tank.pobierzZwrot(Zmienne.TURN_EAST);
                    break;
                case Zmienne.TURN_SOUTH:
                    dx = 1;
                    tank.pobierzZwrot(Zmienne.TURN_SOUTH);
                    break;
                case Zmienne.TURN_WEST:
                    dy = -1;
                    tank.pobierzZwrot(Zmienne.TURN_WEST);
                    break;
            }
            if (getBool(tank.zwrocPolozenieX() + dx, tank.zwrocPolozenieY() + dy)) {
                if (rozkladPlansza[tank.zwrocPolozenieX() + dx][tank.zwrocPolozenieY() + dy] == Zmienne.BONUS_LIFE || rozkladPlansza[tank.zwrocPolozenieX() + dx][tank.zwrocPolozenieY() + dy] == Zmienne.BONUS_LIFE + Zmienne.WOOD) {
                    tank.additionalLife();
                    mW.player[clientAddress.indexOf(TankAddress)].setPower(((Tank) clientTank.elementAt(clientAddress.indexOf(TankAddress))).getLife());
                    state = false;
                }
                komunikat = setRozklad(tank.zwrocPolozenieX() + dx, tank.zwrocPolozenieY() + dy, tank.zwrocPolozenieX(), tank.zwrocPolozenieY(), tank.zwrocKierunek());
                rozkladTank[tank.zwrocPolozenieX() + dx][tank.zwrocPolozenieY() + dy] = clientAddress.indexOf(TankAddress);
                rozkladTank[tank.zwrocPolozenieX()][tank.zwrocPolozenieY()] = -1;
                tank.pobierzX(dx);
                tank.pobierzY(dy);
            }
        }
        return komunikat;
    }

    /**
     *
     * @param i
     * @param j
     * @param direction
     * @return
     */
    public String rotate(int i, int j, int direction)// This was changed when dr Piasecki noticed bug in steering
    {
        values = getObjectDescription(rozkladPlansza[i][j]);
        String Komunikat = "";
        Komunikat += (char) i;
        Komunikat += (char) j;
        if (values[2] != Zmienne.getTANK_IN_WOOD()) {
            rozkladPlansza[i][j] = Zmienne.getTANK() + direction * 10 + values[0] * 100;
        } else {
            rozkladPlansza[i][j] = Zmienne.getTANK_IN_WOOD() + direction * 10 + values[0] * 100;
        }
        Komunikat += (char) rozkladPlansza[i][j];
        return Komunikat;
    }

    //              Mind of the game
    /**
     *
     * @param i
     * @param j
     * @param ki
     * @param kj
     * @param direction
     * @return
     */
    public String setRozklad(int i, int j, int ki, int kj, int direction) {
        values = getObjectDescription(rozkladPlansza[ki][kj]);
        String Komunikat = "";
        Komunikat += (char) i;
        Komunikat += (char) j;
        if (rozkladPlansza[i][j] == Zmienne.BONUS_LIFE || rozkladPlansza[i][j] == Zmienne.BONUS_LIFE + Zmienne.WOOD) {
            rozkladPlansza[i][j] = (rozkladPlansza[i][j] % 10 == Zmienne.WOOD ? Zmienne.getTANK_IN_WOOD() : Zmienne.getTANK()) + direction * 10 + values[0] * 100;
        } else {
            rozkladPlansza[i][j] = (rozkladPlansza[i][j] == Zmienne.WOOD ? Zmienne.getTANK_IN_WOOD() : Zmienne.getTANK()) + direction * 10 + values[0] * 100;
        }
        Komunikat += (char) rozkladPlansza[i][j];
        boolPlansza[i][j] = false;
        Komunikat += (char) ki;
        Komunikat += (char) kj;
        if (values[2] != Zmienne.getTANK_IN_WOOD()) {
            rozkladPlansza[ki][kj] = Zmienne.getFREE();
        } else {
            rozkladPlansza[ki][kj] = Zmienne.getWOOD();
        }
        Komunikat += (char) rozkladPlansza[ki][kj];
        boolPlansza[ki][kj] = true;
        return Komunikat;
    }

    /**
     *
     * @param i
     * @param j
     * @param ki
     * @param kj
     * @param direction
     * @param first_time
     * @return
     */
    public String setRozkladForOpenSpace(int i, int j, int ki, int kj, int direction, boolean first_time) {
        String Komunikat = "";
        Komunikat += (char) i;
        Komunikat += (char) j;
        rozkladPlansza[i][j] = (rozkladPlansza[i][j] % 10 != Zmienne.getWOOD()) ? Zmienne.getMISSILE() + direction * 10 : Zmienne.getWOOD();
        Komunikat += (char) rozkladPlansza[i][j];
        boolPlansza[i][j] = false;
        Komunikat += (char) ki;
        Komunikat += (char) kj;
        if (!first_time) {
            rozkladPlansza[ki][kj] = (rozkladPlansza[ki][kj] != Zmienne.getWOOD()) ? Zmienne.getFREE() : Zmienne.getWOOD();
            boolPlansza[ki][kj] = true;
        } else {
            boolPlansza[ki][kj] = false;
        }
        Komunikat += (char) rozkladPlansza[ki][kj];
        return Komunikat;
    }
    MainWindow mW;

    /**
     *
     * @param _mW
     */
    public void setMW(MainWindow _mW) {
        mW = _mW;
    }

    /**
     *
     * @param i
     * @param j
     * @param ki
     * @param kj
     * @param first_time
     * @return
     */
    public synchronized String setRozkladForDestroyStop(int i, int j, int ki, int kj, boolean first_time) {
        String Komunikat = "";
        Komunikat += (char) i;
        Komunikat += (char) j;
        if (rozkladPlansza[i][j] % 10 == Zmienne.MISSILE) {
            boolPlansza[i][j] = true;
            boolPlansza[ki][kj] = false;
            rozkladPlansza[i][j] = (rozkladPlansza[i][j] != Zmienne.getWOOD()) ? Zmienne.getFREE() : Zmienne.getWOOD();
            Komunikat += (char) rozkladPlansza[i][j];
            rozkladPlansza[ki][kj] = (rozkladPlansza[ki][kj] != Zmienne.getWOOD()) ? Zmienne.getFREE() : Zmienne.getWOOD();
            Komunikat += (char) rozkladPlansza[ki][kj];
            System.out.println("1");
        } else {
            System.out.println("2");
            values = getObjectDescription(rozkladPlansza[i][j]);
            if ((values[2] == Zmienne.TANK) || (values[2] == Zmienne.TANK_IN_WOOD)) {
                System.out.println("3");
                if (((Tank) clientTank.elementAt(rozkladTank[i][j])).isAlive()) {
                    ((Tank) clientTank.elementAt(rozkladTank[i][j])).shot();
                    System.out.println("1");
                    mW.player[rozkladTank[i][j]].setPower(((Tank) clientTank.elementAt(rozkladTank[i][j])).getLife());
                } else {
                    System.out.println("4");
                    mW.player[rozkladTank[i][j]].setPower(0);
                    rozkladPlansza[i][j] = (rozkladPlansza[i][j] != Zmienne.getWOOD()) ? Zmienne.getFREE() : Zmienne.getWOOD();
                    deadPlayers[rozkladTank[i][j]] = true;
                    rozkladTank[i][j] = -1;
                    boolPlansza[i][j] = true;
                }
                Komunikat += (char) rozkladPlansza[i][j];
            } else {
                System.out.println("5");
                rozkladPlansza[i][j] = (rozkladPlansza[i][j] != Zmienne.getWOOD()) ? Zmienne.getFREE() : Zmienne.getWOOD();
                Komunikat += (char) rozkladPlansza[i][j];
                boolPlansza[i][j] = true;
            }
            Komunikat += (char) ki;
            Komunikat += (char) kj;
            if (!first_time) {
                System.out.println("6");
                rozkladPlansza[ki][kj] = (rozkladPlansza[ki][kj] != Zmienne.getWOOD()) ? Zmienne.getFREE() : Zmienne.getWOOD();
                boolPlansza[ki][kj] = true;
            } else {
                System.out.println("7");
                boolPlansza[ki][kj] = false;
            }
            Komunikat += (char) rozkladPlansza[ki][kj];
        }
        return Komunikat;
    }

    /**
     *
     * @param i
     * @param j
     * @param ki
     * @param kj
     * @param first_time
     * @return
     */
    public String setRozkladForStop(int i, int j, int ki, int kj, boolean first_time) {
        String Komunikat = "";
        Komunikat += (char) i;
        Komunikat += (char) j;
        Komunikat += (char) rozkladPlansza[i][j];
        boolPlansza[i][j] = false;
        Komunikat += (char) ki;
        Komunikat += (char) kj;
        if (!first_time) {
            rozkladPlansza[ki][kj] = (rozkladPlansza[ki][kj] != Zmienne.getWOOD()) ? Zmienne.getFREE() : Zmienne.getWOOD();
            boolPlansza[ki][kj] = true;
        } else {
            boolPlansza[ki][kj] = false;
        }
        Komunikat += (char) rozkladPlansza[ki][kj];
        return Komunikat;
    }

    /**
     *
     * @param i
     * @param j
     * @param first_time
     * @return
     */
    public String setRozkladForRange(int i, int j, boolean first_time) {
        String Komunikat = "";
        Komunikat += (char) i;
        Komunikat += (char) j;
        if (!first_time) {
            Komunikat += (char) (rozkladPlansza[i][j] = Zmienne.FREE);
            boolPlansza[i][j] = true;
        } else {
            Komunikat += (char) (rozkladPlansza[i][j]);
            boolPlansza[i][j] = false;
        }
        Komunikat += (char) i;
        Komunikat += (char) j;
        Komunikat += (char) Komunikat.charAt(2);
        return Komunikat;
    }

    /**
     *
     * @param i
     * @param j
     * @return
     */
    public int getRozklad(int i, int j) {
        return rozkladPlansza[i][j];
    }

    /**
     *
     * @param i
     * @param j
     * @return
     */
    public boolean getBool(int i, int j) {
        if (i < 0 || i >= Zmienne.SZEROKOSC || j < 0 || j >= Zmienne.WYSOKOSC) {
            return false;
        }
        return boolPlansza[i][j];
    }

    /**
     *
     * @param i
     * @param j
     * @param direction
     * @param whichPayer
     * @param tankAddress
     */
    public void setTank(int i, int j, int direction, int whichPayer, String tankAddress) {
        if (rozkladPlansza[i][j] == Zmienne.WOOD) {
            rozkladPlansza[i][j] = Zmienne.TANK_IN_WOOD + direction * 10 + whichPayer * 100;
        } else {
            rozkladPlansza[i][j] = Zmienne.TANK + direction * 10 + whichPayer * 100;
        }
        clientAddress.addElement(tankAddress);
        rozkladTank[i][j] = clientAddress.size() - 1;
        clientTank.addElement(new Tank(i, j, direction));
        boolPlansza[i][j] = false;
    }

    /**
     *
     * @return
     */
    public int[][] returnRozkladPlanszy() {
        return rozkladPlansza;
    }

    /**
     *
     * @param address
     */
    public void remove(String address) {
        Tank tank = (Tank) clientTank.elementAt(clientAddress.indexOf(address));
        values = getObjectDescription(rozkladPlansza[tank.zwrocPolozenieX()][tank.zwrocPolozenieY()]);
        if (values[2] == Zmienne.TANK_IN_WOOD) {
            rozkladPlansza[tank.zwrocPolozenieX()][tank.zwrocPolozenieY()] = Zmienne.WOOD;
        } else {
            rozkladPlansza[tank.zwrocPolozenieX()][tank.zwrocPolozenieY()] = Zmienne.FREE;
        }
        rozkladTank[tank.zwrocPolozenieX()][tank.zwrocPolozenieY()] = -1;
        boolPlansza[tank.zwrocPolozenieX()][tank.zwrocPolozenieY()] = true;
        clientTank.removeElementAt(clientAddress.indexOf(address));
        clientAddress.removeElement(address);
    }

    /**
     *
     * @param Address
     * @return
     */
    public Tank getTank(String Address) {
        return (Tank) clientTank.elementAt(clientAddress.indexOf(Address));
    }

    /**
     *
     * @return
     */
    public int size() {
        return clientAddress.size();
    }

    /**
     *
     * @param i
     * @return
     */
    public String elementAt(int i) {
        return (String) clientAddress.elementAt(i);
    }

    /**
     *
     * @return
     */
    public String getStoredRozlad() {
        return kod.kodowanie(returnRozkladPlanszy());
    }

    /**
     *
     * @param i
     * @param j
     * @return
     */
    public boolean getDecision(int i, int j) {
        if (rozkladPlansza[i][j] == Zmienne.STELL_WALL || rozkladPlansza[i][j] == Zmienne.WATER) {
            return true;
        }
        return false;
    }

    /**
     *
     * @param i
     * @param j
     * @return
     */
    public boolean out_of_range(int i, int j) {
        if (i < 0 || i >= Zmienne.SZEROKOSC || j < 0 || j >= Zmienne.WYSOKOSC) {
            return true;
        }
        return false;
    }

    private int[] getObjectDescription(int value) {
        values[0] = value / 100;//postac
        int temp_value = value / 10;
        values[1] = temp_value % 10;//kierunek
        values[2] = value % 10;//co
        return values;
    }

    /**
     *
     */
    public void to() {
        System.out.println("[ ");
        for (int i = 0; i < Zmienne.SZEROKOSC; i++) {
            System.out.println("");
            for (int j = 0; j < Zmienne.WYSOKOSC; j++) {
                if (boolPlansza[i][j]) {
                    System.out.print(1 + ", ");
                } else {
                    System.out.print(0 + ", ");
                }

            }
        }
        System.out.println("]");
        System.out.println("[ ");
        for (int i = 0; i < Zmienne.SZEROKOSC; i++) {
            System.out.println("");
            for (int j = 0; j < Zmienne.WYSOKOSC; j++) {
                if (rozkladPlansza[i][j] > 100) {
                    System.out.print("#");
                } else {
                    System.out.print(rozkladPlansza[i][j] + ", ");
                }
            }
        }
        System.out.println("]");

    }

    public void uruchomWatekStrzalu(final int pozycja, final String btAdress) {
        if (freeToKill[pozycja]) {
            freeToKill[pozycja] = false;
            Thread flight = new Thread() {

                int iO = pozycja;
                boolean status = true;

                @Override
                public void run() {
                    pociski[iO] = new Pocisk();
                    Tank tank = getTank(btAdress);
                    pociski[iO].setX(tank.zwrocPolozenieX());
                    pociski[iO].setY(tank.zwrocPolozenieY());
                    pociski[iO].setDirection(tank.zwrocKierunek());
                    pociski[iO].increment();



                    while (getBool(pociski[iO].getIncrementedX(), pociski[iO].getIncrementedY())) {

                        listener.sendToAll(setRozkladForOpenSpace(pociski[iO].getIncrementedX(), pociski[iO].getIncrementedY(),
                                pociski[iO].getPolozenieX(), pociski[iO].getPolozenieY(), tank.zwrocKierunek(), status));
                        status = false;
                        pociski[iO].increment();
                        try {
                            Thread.sleep(110);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    if (out_of_range(pociski[iO].getIncrementedX(), pociski[iO].getIncrementedY())) {
                        listener.sendToAll(setRozkladForRange(pociski[iO].getPolozenieX(), pociski[iO].getPolozenieY(),
                                status));
                    } else if (getDecision(pociski[iO].getIncrementedX(), pociski[iO].getIncrementedY())) {
                        listener.sendToAll(setRozkladForStop(pociski[iO].getIncrementedX(), pociski[iO].getIncrementedY(),
                                pociski[iO].getPolozenieX(), pociski[iO].getPolozenieY(),
                                status));
                    } else {
                        listener.sendToAll(setRozkladForDestroyStop(pociski[iO].getIncrementedX(), pociski[iO].getIncrementedY(),
                                pociski[iO].getPolozenieX(), pociski[iO].getPolozenieY(),
                                status));
                    }
                    freeToKill[iO] = true;
                    pociski[iO] = null;
                }
            };
            flight.start();
        }

    }

    public void uruchomWatekZyc() {
        Thread bonusManager = new Thread(new InvokeNewBonus());
        bonusManager.start();
    }

    private class InvokeNewBonus implements Runnable {

        private int[] value = new int[3];

        public void run() {
            while (true) {
                try {
                    Thread.sleep(Zmienne.BONUS_SLEEP_TIME);
                } catch (InterruptedException e) {
                    System.err.println("Unable to sleep so long");
                }
                value = getNewTankPosition();
                value[2] = getRozklad(value[0], value[1]);
                Thread newBonus = new Thread(new LifeManager(value[0], value[1], value[2]));
                newBonus.start();
            }
        }
    }

    public class LifeManager implements Runnable {

        private int polozenieX;
        private int polozenieY;
        private int initialValue;
        private long startTime;
        private boolean state;

        /**
         *
         * @param x
         * @param y
         * @param initialValue
         */
        public LifeManager(int x, int y, int initialValue) {
            this.polozenieX = x;
            this.polozenieY = y;
            this.initialValue = initialValue;
            setBonus(x, y, Zmienne.BONUS_LIFE + initialValue);
            String komunikat = "";
            komunikat += (char) x;
            komunikat += (char) y;
            komunikat += (char) (Zmienne.BONUS_LIFE + initialValue);
            listener.sendToAll(komunikat);
            state = true;
            startTime = System.currentTimeMillis();
        }

        /**
         *
         * @param _state
         */
        public void changeState(boolean _state) {
            this.state = _state;
        }

        synchronized public void run() {
            long differenceTime;
            state = true;
            while (state) {
                differenceTime = System.currentTimeMillis() - this.startTime;
                if (differenceTime > Zmienne.BONUS_SLEEP_TIME / 5) {
                    state = false;
                    break;
                } else {
                    try {
                        Thread.sleep(differenceTime);
                    } catch (InterruptedException e) {
                        System.err.println("Insomnia");
                    }
                }
            }
            if (state) {
                System.out.println("Zmieniam");
                setBonus(polozenieX, polozenieY, initialValue);
                String komunikat = "";
                komunikat += (char) polozenieX;
                komunikat += (char) polozenieY;
                komunikat += (char) initialValue;
                listener.sendToAll(komunikat);
            }
        }
    }

}
